package com.eric.code.oauth2.api;

import com.eric.code.oauth2.Oauth2Constants;
import com.eric.code.oauth2.common.BaseController;
import com.eric.code.oauth2.login.LoginService;
import com.eric.code.oauth2.model.App;
import com.eric.code.oauth2.model.User;
import com.eric.code.oauth2.user.UserService;
import com.jfinal.kit.Ret;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.apache.oltu.oauth2.rs.response.OAuthRSResponse;

import javax.servlet.http.HttpServletResponse;
import java.net.URISyntaxException;

/**
 * Created by eric on 17-3-21.
 * 这个类的任何协议相关的参数不要修改
 */
public class ApiOauthController extends BaseController {

    private LoginService loginService = LoginService.me;
    private OauthService oauthService = OauthService.me;
    private UserService userService = UserService.me;
    /**
     * 请求授权
     * 需要的参数如下：
     *  client_id  应用id
     *  response_type 返回授权码的标识 code
     *  redirect_uri 回调地址
     * 如果客户端授权成功之后会通过回调传送code  http://×××××/?code=63910432da9186b22b1ad888d55ae8ae
     *
     */
    public void authorize() throws URISyntaxException, OAuthSystemException {
        try {

            //构建OAuth 授权请求
            OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(getRequest());

            System.out.println("client_id : "+oauthRequest.getClientId());
            //检查传入的客户端id是否正确
            if (!oauthService.checkClientId(oauthRequest.getClientId())) {
                OAuthResponse response =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription(Oauth2Constants.INVALID_CLIENT_ID)
                                .buildJSONMessage();
                renderError(response.getResponseStatus());
                return ;
            }

            System.out.println("client_id : "+oauthRequest.getClientId());
            User user = getLoginUser();
            //如果用户没有登录，跳转到登陆页面
            if(user == null) {//登录失败时跳转到登陆页面
                setAttr("client_id",getPara("client_id"));
                setAttr("response_type",getPara("response_type"));
                setAttr("redirect_uri",getPara("redirect_uri"));
                setAttr("state",getPara("state"));
                setAttr("client", oauthService.findByClientId(oauthRequest.getClientId()));
                render("login.html");
                return ;
            }

            System.out.println("用户已经登录～～");
            long userId = user.getId(); //获取用户名
            //生成授权码
            String authorizationCode = null;
            //responseType目前仅支持CODE，另外还有TOKEN
            String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
            if (responseType.equals(ResponseType.CODE.toString())) {
                OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
                authorizationCode = oauthIssuerImpl.authorizationCode();
                oauthService.setAuthCode(authorizationCode, userId);
            }

            //进行OAuth响应构建
            OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                    OAuthASResponse.authorizationResponse(getRequest(), HttpServletResponse.SC_FOUND);
            //设置授权码
            builder.setCode(authorizationCode);
            //得到到客户端重定向地址
            String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);

            //构建响应
            final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();

            /*
             * 这里获取到的 response.getLocationUri 已经是带有code的重定向url了
             * 如 : http://gerrit.com?code=e9446f12b3b880fb2333802eae092704
             */
            System.out.println("response ～～ "+response.getLocationUri());
            //根据OAuthResponse返回ResponseEntity响应
            redirect(response.getLocationUri());
        } catch (OAuthProblemException e) {

            //出错处理
            String redirectUri = e.getRedirectUri();
            if (OAuthUtils.isEmpty(redirectUri)) {
                //告诉客户端没有传入redirectUri直接报错
                renderError(404);
                return ;
            }
            //返回错误消息（如?error=）
            final OAuthResponse response =
                    OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
                            .error(e).location(redirectUri).buildQueryMessage();
            System.out.println("response exception ～～ "+response.getLocationUri());
            render(response.getLocationUri());
        }
    }

    /**
     * 专门针对oauth的登录
     * 登录成功后需要重定向到 authorize 进行再一次的认证
     */
    public void login(){
        Ret ret = loginService.login(getPara("email"), getPara("password"));
        if(ret.isOk()){
            String sessionId = ret.getStr(LoginService.sessionIdName);
            setCookie(LoginService.sessionIdName,sessionId,-1);
            redirect("/oauth2/authorize?client_id="+getPara("client_id")+"&response_type="+getPara("response_type")+"&redirect_uri="+getPara("redirect_uri")+"&state="+getPara("state"));
        }else{
            setAttr("ret",ret);
            setAttr("client", oauthService.findByClientId(getPara("client_id")));
            render("login.html");
        }
    }

    /**
     * 这个请求一定使用的POST
     * 客户端通过刚才的code参数以及以下参数获取真正的token
     * client_id
     * client_secret  可以认为密码
     * grant_type  authorization_code
     * code
     * redirect_uri
     * 这一步执行成功之后返回  {"expires_in":3600,"access_token":"223ae05dfbb0794396fb60a0960c197e"}
     * 客户端能够真正拿到access_token
     */
    public void accessToken()throws URISyntaxException, OAuthSystemException{
        try {
            //构建OAuth请求
            OAuthTokenRequest oauthRequest = new OAuthTokenRequest(getRequest());

            App app = oauthService.findByClientId(oauthRequest.getClientId());

            //检查提交的客户端id是否正确
            if (app == null) {
                OAuthResponse response =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription(Oauth2Constants.INVALID_CLIENT_ID)
                                .buildJSONMessage();
                System.out.println("checkClientId fail :"+response.getBody());
                renderError(response.getResponseStatus());
                return ;
            }

            // 检查客户端安全KEY是否正确
            if (!app.getClientSecret().equals(oauthRequest.getClientSecret())) {
                OAuthResponse response =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                                .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
                                .setErrorDescription(Oauth2Constants.INVALID_CLIENT_ID)
                                .buildJSONMessage();
                System.out.println("checkClientSecret fail :"+response.getBody());
                renderError(response.getResponseStatus());
                return ;
            }

            String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
            // 检查验证类型，此处只检查AUTHORIZATION_CODE类型，其他的还有PASSWORD或REFRESH_TOKEN
            if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
                if (!oauthService.checkAuthCode(authCode)) {
                    OAuthResponse response = OAuthASResponse
                            .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                            .setError(OAuthError.TokenResponse.INVALID_GRANT)
                            .setErrorDescription(Oauth2Constants.INVALID_AUTH_CODE)
                            .buildJSONMessage();
                    System.out.println("checkAuthCode fail :"+response.getBody());
                    renderError(response.getResponseStatus());
                    return ;
                }
            }

            //生成Access Token
            OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
            final String accessToken = oauthIssuerImpl.accessToken();

            oauthService.setAccessToken(accessToken,oauthService.getUserIdByCode(authCode));


            //生成OAuth响应
            OAuthResponse response = OAuthASResponse
                    .tokenResponse(HttpServletResponse.SC_OK)
                    .setAccessToken(accessToken)
                    .setExpiresIn(String.valueOf(oauthService.getExpireIn()))
                    .buildJSONMessage();

            //根据OAuthResponse生成ResponseEntity
            System.out.println("response -> "+response.getResponseStatus()+"  :  "+response.getBody());
            renderJson(response.getBody());
        } catch (OAuthProblemException e) {
            //构建错误响应
            OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
                    .buildJSONMessage();
            System.out.println("exception -> "+res.getResponseStatus()+"  :  "+res.getBody());
            renderJson(res.getBody());
        }
    }

    /**
     * 通过access_token 获取用户资源
     * 对于gerrit来将 需要包含
     * id 用户的id
     * name
     * username
     * email
     */
    public void userInfo()throws OAuthSystemException{
        try {
            //构建OAuth资源请求
            OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(getRequest(), ParameterStyle.QUERY);
            //获取Access Token
            String accessToken = oauthRequest.getAccessToken();


            //验证Access Token
            if (!oauthService.checkAccessToken(accessToken)) {
                // 如果不存在/过期了，返回未验证错误，需重新验证
                OAuthResponse oauthResponse = OAuthRSResponse
                        .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                        .setRealm(Oauth2Constants.RESOURCE_SERVER_NAME)
                        .setError(OAuthError.ResourceResponse.INVALID_TOKEN)
                        .buildHeaderMessage();
                Status status = new Status();
                status.setCode(Status.UNAUTHORIZED);
                status.setMsg(Oauth2Constants.INVALID_ACCESS_TOKEN);
                renderJson(status);
                return ;
            }
            //获取用户名
            long userId = oauthService.getUserIdByAccessToken(accessToken);
            User user = userService.findById(userId);
            renderJson(user);
        } catch (OAuthProblemException e) {
            //检查是否设置了错误码
            String errorCode = e.getError();
            if (OAuthUtils.isEmpty(errorCode)) {
                OAuthResponse oauthResponse = OAuthRSResponse
                        .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                        .setRealm(Oauth2Constants.RESOURCE_SERVER_NAME)
                        .buildHeaderMessage();
                getResponse().addHeader(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
                getResponse().addHeader(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
                renderHtml("");
//                return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
            }
            OAuthResponse oauthResponse = OAuthRSResponse
                    .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setRealm(Oauth2Constants.RESOURCE_SERVER_NAME)
                    .setError(e.getError())
                    .setErrorDescription(e.getDescription())
                    .setErrorUri(e.getUri())
                    .buildHeaderMessage();
            getResponse().addHeader(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
            renderHtml("");
//            return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }
    }
}
